#ifndef _ARRAY_LIST_
#define _ARRAY_LIST_

#include "core.h"

namespace core {

template <typename Type>
class array
{
public:
	array()
	{
		capacity_ = defaultCapacity_;
		array_ = new Type[capacity_];
		count_ = 0;
	}

	explicit array(int capacity)
	{
		Assert(capacity > 0);
		capacity_ = capacity;
		array_ = new Type[capacity_];
		count_ = 0;
	}

	~array(){ delete[] array_; }

	void clone(array& destination) const
	{
		delete[] destination.array_;
		destination.capacity_ = capacity_;
		destination.array_ = new Type[capacity_];
		for(int i = 0; i < count_; i++){ destination.array_[i] = array_[i]; }
		destination.count_ = count_;
	}

	int size() const{ return count_; }
	bool empty() const{ return (count_ == 0); }
	
	Type& get(int index) const
	{
		Assert(index >= 0);
		Assert(index < count_);
		return array_[index];
	}

	Type& operator [](int index) const
	{
		Assert(index >= 0);
		Assert(index < count_);
		return array_[index];
	}

	int getCapacity() const{ return capacity_; }

	int indexOf(const Type& searchValue) const
	{
		for(int i = 0; i < count_; i++){
			if(array_[i] == searchValue){ return i; }
		}
		return -1;
	}

	int lastIndexOf(const Type& searchValue) const
	{
		for(int i = count_ - 1; i >= 0; i--)
		{
			if(array_[i] == searchValue){ return i; }
		}
		return -1;
	}

	void push_back(const Type& value)
	{
		if((count_ + 1) > capacity_){ resize(capacity_ * 2); }
		array_[count_] = value;
		count_++;
	}

	void push_back(int index, const Type& value) const
	{
		Assert(index >= 0);
		Assert(index < count_);
		array_[index] = value;
	}

	Type remove(int index)
	{
		Assert(index >= 0);
		Assert(index < count_);
		Assert(count_ > 0);
		Type result = array_[index];
		count_--;
		for(int i = index; i < count_; i++){ array_[i] = array_[i + 1]; }
		return result;
	}

	int removeByValue(const Type& removeValue)
	{
		for(int i = count_ - 1; i >= 0; i--)
		{
			if(array_[i] == removeValue)
			{
				remove(i);
				return i;
			}
		}
		return -1;
	}

	void clear() { count_ = 0; }

	void clear(int capacity)
	{
		Assert(capacity >= 0);
		if(capacity <= 0){ capacity = 1; }
		delete[] array_;
		capacity_ = capacity;
		array_ = new Type[capacity_];
		count_ = 0;
	}

	void setCapacity(int newCapacity)
	{
		Assert(newCapacity >= count_);
		resize(newCapacity);
	}

	void trim()
	{
		if(count_ == 0){ resize(1);}
		else{ resize(count_); }
	}

	void sort( int(*compare)(const Type*, const Type*) )
	{
		qsort(array_, count_, sizeof(Type),
			(int(*)(const void*, const void*))compare);
	}

	Type* search(Type key, int(*compare)(const Type*, const Type*) )
	{
		return (Type*)bsearch(&key, array_, count_, sizeof(Type),
			(int(*)(const void*, const void*))compare);
	}

private:
	void resize(int newCapacity)
	{
		Assert(newCapacity > 0);
		Assert(newCapacity >= count_);
		Type* newArray = new Type[newCapacity];

		for(int i = 0; i < count_; i++){ newArray[i] = array_[i]; }
		delete[] array_;
		array_ = newArray;
		capacity_ = newCapacity;
	}

	array(const array& copy);
	void operator =(const array& copy);

	Type* array_;
	int count_;
	int capacity_;
	static const int defaultCapacity_ = 16;
};

}
#endif
